﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using System.Diagnostics;

namespace Test
{
    public partial class Form2 : Form
    {
        public enum State
        {
            notStarted,
            started,
            paused,
            finished
        }

        // 第零步 创建类_Copy
        public class CopyFile
        {
            public string SrcFile;      //源文件
            public string DstFile;      //目标文件
            public Stream stream;       //流文件
            public byte[] buffer;       //缓存
            public ulong position;      //当前文件复制位置
            public ulong Size;          //当前文件总大小
            public IAsyncResult ia;
        }

        // 第一步 添加成员属性
        ulong totalSize;                 //读取文件总大小
        ulong position;                  //当前读取位置
        State state = State.notStarted;  //复制状态
        ulong BUFFER_SIZE = 1024;        //缓存大小 1k
        string srcPath;
        string destPath;
        List<CopyFile> CopyList = new List<CopyFile>();          //复制列表

        public Form2()
        {
            InitializeComponent();
            this.Load += (sender, e) =>
                {
                    label1.Text = "窗口加载完毕";
                };
        }

        private void AsyncCopyFile(IAsyncResult ar)     // callback function
        {
            Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 异步线程 开始复制");
            int readedLength;
            CopyFile copy = (CopyFile)ar.AsyncState;
            try
            {
                //锁顶文件流
                lock (copy.stream)
                {
                    //当文件流读取完毕，获得读取的大小
                    readedLength = copy.stream.EndRead(ar);
                }
                //写入文件
                FileStream fsWriter = new FileStream(copy.DstFile, FileMode.Append, FileAccess.Write);
                fsWriter.Write(copy.buffer, 0, copy.buffer.Length);
                fsWriter.Close();
                //当前文件流位置
                lock (copy.stream)
                {
                    copy.position += (ulong)readedLength;
                    position += (ulong)readedLength;
                }
                //响应进度条
                MethodInvoker m = new MethodInvoker(SynchProgressBar);
                m.BeginInvoke(null, null);
                //读取完毕
                if (copy.position >= copy.Size)
                {
                    //关闭文件流
                    copy.stream.Close();
                    //删除备份文件
                    File.Delete(copy.DstFile + "_upgrade");
                    Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 异步线程 退出AsyncCopyFile方法");
                    Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 异步线程 copy.position=" + copy.position);
                    Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 异步线程     copy.Size=" + copy.Size);
                    return;
                }
                
                //继续读取下个缓存
                lock (copy.stream)
                {
                    //剩下的文件大小
                    ulong leftSize = (copy.Size - copy.position);
                    //如果比缓存小，就将缓存大小设置为剩下的文件大小
                    if (leftSize < (ulong)copy.buffer.Length)
                        copy.buffer = new byte[leftSize];
                    //继续异步读取
                    Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 异步线程 copy.buffer.Length=" + copy.buffer.Length);
                    copy.ia = copy.stream.BeginRead(copy.buffer, 0, copy.buffer.Length, new AsyncCallback(AsyncCopyFile), copy);
                    
                }
            }
            catch (Exception Ex)
            {
                Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 异步线程 异常捕获");
                Debug.WriteLine("异步线程 异常信息: " + Ex.Message);
                //出错，恢复备份文件
                //File.Copy(copy.DstFile + "_upgrade", copy.DstFile, true);
                //MessageBox.Show("更新出错" + Ex.ToString());
            }
        }

        public delegate void delegate_SynProgress();
        private void SynchProgressBar()
        {
            if (this.InvokeRequired)
            {
                Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 必须调用Invoke方法");
                this.Invoke(new delegate_SynProgress(SynchProgressBar));
            }
            else
            {
                if (progressBar1.Enabled)
                {
                    Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 响应进度条");
                    Debug.WriteLine(DateTime.Now.ToLongTimeString() + "  position=" + position);
                    Debug.WriteLine(DateTime.Now.ToLongTimeString() + " totalSize=" + totalSize);
                    if (position >= totalSize)
                    {
                        progressBar1.Value = 100;
                        button3.Text = "开始复制";
                        state = State.finished;
                        position = 0;   // 多线程判断
                        Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 复制完成！");
                        MessageBox.Show(this, DateTime.Now.ToLongTimeString() + " 导出完成！", "提示！");
                    }
                    if (state != State.finished)
                        progressBar1.Value = (int)(1.0 * position / totalSize * 100);
                }
                else
                {
                    Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 不响应进度条");
                    Debug.WriteLine(DateTime.Now.ToLongTimeString() + "  position=" + position);
                    Debug.WriteLine(DateTime.Now.ToLongTimeString() + " totalSize=" + totalSize);
                }
            }
        }

        /// <summary>
        /// 获取源文件中所有文件路径及创建相应目标文件路径
        /// </summary>
        /// <param name="srcPath"></param>
        /// <param name="destPath"></param>
        public List<CopyFile> GetCopyList(string srcPath, string destPath)
        {
            Debug.WriteLine(string.Format("{0}\n  源文件夹:{1}\n目标文件夹:{2}",DateTime.Now.ToLongTimeString(),
                srcPath,destPath));
            if (!Directory.Exists(srcPath))
            {
                return null;
            }
            char[] separator = {'\\'};
            string srcLastDir = srcPath.Split(separator)[srcPath.Split(separator).Length - 2];      //获取最后一级目录名
            string destLastDir = destPath.Split(separator)[destPath.Split(separator).Length - 2];   //
            Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 比较目录最后一级 " + srcLastDir + " " + destLastDir);
            if (!srcLastDir.Equals(destLastDir))
            {
                destPath += srcLastDir + "\\"; 
                Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 目标文件夹:" + destPath);
            }
            if (!Directory.Exists(destPath))
            {
                Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 目标文件夹不存在,创建目标文件夹");
                Directory.CreateDirectory(destPath);
            }

            List<CopyFile> copyList = new List<CopyFile>();       //保存文件信息 返回值
            Queue<string> pathQueue = new Queue<string>();        //队列保存目录
            pathQueue.Enqueue(srcPath);
            while (pathQueue.Count > 0)
            {
                DirectoryInfo diParent = new DirectoryInfo(pathQueue.Dequeue());
                foreach (DirectoryInfo diChild in diParent.GetDirectories())
                {
                    pathQueue.Enqueue(diChild.FullName);
                    string newDir = diChild.FullName.Replace(srcPath, destPath);
                    if (!Directory.Exists(newDir))
                    {
                        Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 为目标文件夹创建子文件夹: " + newDir);
                        Directory.CreateDirectory(newDir);
                    }
                }
                foreach (FileInfo fi in diParent.GetFiles())
                {
                    Debug.WriteLine(DateTime.Now.ToLongTimeString() + "   源文件: " + fi.FullName);
                    Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 目标文件: " + fi.FullName.Replace(srcPath, destPath));
                    CopyFile copy = new CopyFile();
                    copy.SrcFile = fi.FullName;
                    copy.DstFile = fi.FullName.Replace(srcPath, destPath);
                    copyList.Add(copy);
                }
            }
            Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 需复制文件数: " + copyList.Count);
            return copyList;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            folderBrowserDialog.ShowDialog();
            textBox1.Text = folderBrowserDialog.SelectedPath + "\\";
            srcPath = textBox1.Text.Trim();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            folderBrowserDialog.ShowDialog();
            textBox2.Text = folderBrowserDialog.SelectedPath + "\\";
            destPath = textBox2.Text.Trim();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            Button btn = sender as Button;
            string btnContent = btn.Text;
            if ("开始复制".Equals(btnContent))
            {
                progressBar1.Value = 0;
                btn.Text = "取 消";
                //判断路径格式是否正确
                if ("".Equals(srcPath) || "".Equals(destPath))
                {
                    MessageBox.Show(this,"请选取路径!","提示！");
                }
                CopyList = GetCopyList(srcPath, destPath);

                //如果更新列表没有值，则退出,运行程序
                if (CopyList.Count <= 0)
                {
                    MessageBox.Show(this, "没有文件需复制", "提示！");
                    return;
                }
                totalSize = 0;
                state = State.started;

                Debug.WriteLine(DateTime.Now.ToLongTimeString() + " CopyList.Count=" + CopyList.Count);
                for (int i = 0; i < CopyList.Count; i++)
                {
                    Debug.WriteLine(DateTime.Now.ToLongTimeString() + "循环 i=" + i + " 开始");
                    try
                    {
                        //文件不存在，继续下个文件
                        if (!File.Exists(CopyList[i].SrcFile))
                            continue;
                        //目标文件已经存在，先复制一个备份
                        if (File.Exists(CopyList[i].DstFile))
                        {
                            Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 目标文件已经存在，先复制一个备份");
                            File.Copy(CopyList[i].DstFile, CopyList[i].DstFile + "_upgrade", true);
                            File.Delete(CopyList[i].DstFile);
                        }
                        FileStream fs = new FileStream(CopyList[i].SrcFile, FileMode.Open, FileAccess.Read);
                        Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 读取文件大小: " + fs.Length);
                        totalSize += (ulong)fs.Length;
                        CopyList[i].Size = (ulong)fs.Length;
                        CopyList[i].stream = fs;
                        //这里是设置缓存的大小，可以根据需要修改逻辑
                        if (CopyList[i].Size < 10240)   //10k
                        {
                            BUFFER_SIZE = CopyList[i].Size;
                        }
                        else if (CopyList[i].Size < 1024 * 1024)   //1M
                        {
                            BUFFER_SIZE = CopyList[i].Size / 10;
                        }
                        else if (CopyList[i].Size < 1024 * 1024 * 1024)  //1G
                        {
                            BUFFER_SIZE = CopyList[i].Size / 1000;
                        }
                        else                                            //大于1G
                        {
                            BUFFER_SIZE = CopyList[i].Size / 10000;
                        }
                        Debug.WriteLine(DateTime.Now.ToLongTimeString() + "   文件大小 " + CopyList[i].Size);
                        Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 缓冲区大小 " + BUFFER_SIZE);
                        //缓存的大小要设置的比总大小要小，否则无法读取
                        if (totalSize > BUFFER_SIZE)
                        {
                            CopyList[i].buffer = new byte[BUFFER_SIZE];
                            //异步调用读取文件流
                            Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 异步调用读取文件流");

                            CopyList[i].ia = CopyList[i].stream.BeginRead(CopyList[i].buffer, 0, (int)BUFFER_SIZE,
                                new AsyncCallback(AsyncCopyFile), CopyList[i]);
                        }
                        else
                        {
                            Debug.WriteLine(DateTime.Now.ToLongTimeString() + " fs关闭");
                            fs.Close();
                        }

                    }
                    catch (Exception Ex)
                    {
                        Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 异常捕获");
                        Debug.WriteLine("异常信息: " + Ex.Message);
                        //出错，恢复备份文件
                        //File.Copy(CopyList[i].DstFile + "_upgrade", CopyList[i].DstFile, true);
                        //MessageBox.Show("更新出错" + Ex.ToString());
                    }
                    Debug.WriteLine(DateTime.Now.ToLongTimeString() + "循环 i=" + i + " 结束");
                }
            }
            else if ("取 消".Equals(btnContent))
            {
                progressBar1.Enabled = false;
                state = State.paused;
                DialogResult result = MessageBox.Show(this, "复制还未完成，是否取消？", "提示！", MessageBoxButtons.OKCancel);
                if (DialogResult.OK.Equals(result))
                {
                    this.Close();
                }
                else
                {
                    progressBar1.Enabled = true;
                    state = State.started;
                }
            }
        }

        private void Form2_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (state == State.started)
            {
                progressBar1.Enabled = false;
                state = State.paused;
                DialogResult result = MessageBox.Show(this,"复制还未完成，现在退出将导致复制失败，确定退出？","提示！",MessageBoxButtons.OKCancel);
                if (DialogResult.OK.Equals(result))
                {
                    try
                    {
                        this.Owner.Close();
                    }
                    catch(Exception ex)
                    {
                        Debug.WriteLine(DateTime.Now.ToLongTimeString() + " 关闭异常");
                        Debug.WriteLine(ex.Message);
                    }
                }
                else
                {
                    progressBar1.Enabled = true;
                    state = State.started;
                }
            }
        }

        private void Form2_FormClosed(object sender, FormClosedEventArgs e)
        {
            foreach(CopyFile c in CopyList)
            {
                if(c.stream != null)
                {
                    c.stream.Close();
                }
                if (c.buffer != null)
                {
                    c.buffer = null;
                }
            }
        }
    }
}
